#include "gaussseidelsolver.h"
#include <cmath>
#include <cstdlib>
#include <QProcess>
#include <iostream>
#include <QString>
#include <QByteArray>
#include <QtGui/QApplication>
#include <QDir>
#include <cstdio>
//#include <cmath>
//#include "Python.h"
using namespace std;


GaussSeidelSolver::GaussSeidelSolver(int _n,double mu1, double e1, QObject * parent):
        BaseGaussSeidelSolver(_n, mu1, e1, parent)/*, k((n+2)*(2*n+1))*/,t(sqrt(3))//,k(_m*(2*_n+1))
{
    k = m*(2*n+1);
    QObject *par;
    par = new QObject();
    QProcess proc(par);
    QStringList arguments;

    proc.setWorkingDirectory(qApp->applicationDirPath() + QDir::separator());
    proc.start("python coeffs.py");
    proc.waitForStarted();
    proc.write(QString("%1\n").arg(1).toStdString().c_str());
    proc.write(QString("%1\n").arg(n).toStdString().c_str());
    proc.write(QString("%1\n").arg(mu1).toStdString().c_str());
    proc.write(QString("%1\n").arg(e1).toStdString().c_str());
    //proc.closeWriteChannel();

    proc.waitForReadyRead();
    zone_count = QString(proc.readLine()).toInt();
    //cout << zone_count << endl;


    //coeffs_list = new Zone[zone_count];
    //coeffs_map = new int[k];

    for (int zone = 0; zone < zone_count; zone++)
    {
        Zone temp(2);
        for (int i = 0; i < 2; i++)
        {
            temp[i].resize(2);
            for (int j = 0; j < 2; j++)
            {
                temp[i][j].resize(7);
                for (int p=0; p < 7; p++)
                {
                    proc.waitForReadyRead();
                    temp[i][j][p] = QString(proc.readLine()).toDouble();
                    //coeffs_list[zone].c[i][j][p] = QString(proc.readLine()).toDouble();
                    //cout << coeffs_list[zone].c[i][j][p] << " ";
                    //cout << temp[i][j][p] << " ";
                }
                //cout << endl;
            }

        }
        coeffs_list.push_back(temp);
        //cout << endl;
    }
    // reading coefficients map
    for (int i = 0; i < k; i++)
    {
        proc.waitForReadyRead();
        coeffs_map.push_back(QString(proc.readLine()).toInt());
        //coeffs_map[i] =QString(proc.readLine()).toInt();
        //cout << coeffs_map[i] << endl;
    }

//    x = new double[2*k];


    x.resize(2*k);
    for  (int i = 0; i < k; i++)
    {
        if (coeffs_map[i] != -1)
        {
            //x[i] = ((double)random() / RAND_MAX - 0.5);
            //x[i+k] = ((double)random() / RAND_MAX - 0.5);
            x[i] = x[i+k] = 0.0;
        }
        else
        {
            x[i] = x[i+k] = 0.0;
        }
    }
    cout << QString(proc.readLine()).toStdString() << endl;
    proc.waitForFinished();
}





GaussSeidelSolver::~GaussSeidelSolver()
{
//    if (x != 0)
//        delete [] x;
//    if (coeffs_map != 0)
//        delete [] coeffs_map;
//    if (coeffs_list != 0)
//        delete[] coeffs_list;
}

//int GaussSeidelSolver::solve()
//{
//    double dx = 1000000;
//    int i = 0;
//    bool ok = true;
//    while (dx > eps)
//    {
//        dx = iteration();
//        cout << dx << endl;
//        i++;
//        if (i > 1000000)
//        {
//            ok  = false;
//            //out << "Process stopped because too many iterations" << endl;
//            break;
//        }
//        if (dx > 1000000)
//        {
//            ok = false;
//            //out << "Process stopped because too big divergention" << endl;
//            break;
//        }
//    }
//    if (ok)
//    {
//        //out << "I'm done" << endl;
//    }
//    //for (int i = 0; i < 2* k; i++)
//        //out << x[i] << endl;
//    return i;
//}

double GaussSeidelSolver::iteration()
{
    double maxdx = 0;
    for (int i = 0; i < k; i++)
    {
        Zone  b;
        if (coeffs_map[i] == -1)
            continue;
        b = coeffs_list[coeffs_map[i]];
//        for (int ii = 0; ii < 2; ii++)
//        {
//            for (int j = 0; j < 2; j++)
//            {
//                for (int p = 0; p < 7; p++)
//                    b[ii][j][p] = coeffs_list[coeffs_map[i]][ii][j][p];
//            }
//        }

        int column = i % m;
        int row = i / m;

        double x_new = 0, y_new = 0;
        double newdx = 0;
        if (row == 1)
        {
            x[i] = 0;
            x[i+k] = 0;
            continue ;
        }
        else
        {
            double sum = 0;
            double gama = 0;
            sum =                        x[i+1]* b[0][0][1] +   x[i-m+1]* b[0][0][2] +   x[i-m]* b[0][0][3]+   x[i-1]* b[0][0][4] +   x[i+m-1]* b[0][0][5] +   x[i+m]* b[0][0][6] +
                  + x[i+k]* b[0][1][0] + x[i+1+k]* b[0][1][1] + x[i-m+1+k]* b[0][1][2] + x[i-m+k]* b[0][1][3] + x[i-1+k]* b[0][1][4] + x[i+m-1+k]* b[0][1][5] + x[i+m+k]* b[0][1][6];
            if (column == 1)
            {
                if (row == 1)
                    gama = sum - f1*h/2;
                else if (row < n)
                    gama = sum - f1*h;
                else if (row == n)
                    gama = sum - f1*(h+h/t)/2.0;
                else if (row < 2*n-1)
                    gama = sum - f1*h/t;
                else
                    gama = sum - f1*h/(2*t);
            }
            else if (column == n)
            {
                if (row == 1)
                    gama = sum - f2*h/2;
                else if (row < n)
                    gama = sum - f2*h;
                else
                    gama = sum - f2*h/2;
            }
            else
            {
                gama = sum;
            }
            x_new = -(gama) / b[0][0][0];
            newdx = abs(x_new - x[i]);
            if (newdx > maxdx)
                maxdx = newdx;
            x[i] = x_new;

            sum = x[i]*b[1][0][0] + x[i+1]*b[1][0][1] +   x[i-m+1]*b[1][0][2] +   x[i-m]*b[1][0][3] +   x[i-1]*b[1][0][4] +   x[i+m-1]*b[1][0][5] +   x[i+m]*b[1][0][6] +
                                  + x[i+1+k]*b[1][1][1] + x[i-m+1+k]*b[1][1][2] + x[i-m+k]*b[1][1][3] + x[i-1+k]*b[1][1][4] + x[i+m-1+k]*b[1][1][5] + x[i+m+k]*b[1][1][6];
            gama = sum;
            y_new = -(gama) / b[1][1][0];
            newdx = abs(y_new - x[i+k]);
            if (newdx > maxdx)
                maxdx = newdx;
            x[i+k] = y_new;
        }
//        if (row == 1)
//            x[i+k] = 0;
//        else
//        {
//            double sum = x[i]*b[1][0][0] + x[i+1]*b[1][0][1] +   x[i-m+1]*b[1][0][2] +   x[i-m]*b[1][0][3] +   x[i-1]*b[1][0][4] +   x[i+m-1]*b[1][0][5] +   x[i+m]*b[1][0][6] +
//                                         + x[i+1+k]*b[1][1][1] + x[i-m+1+k]*b[1][1][2] + x[i-m+k]*b[1][1][3] + x[i-1+k]*b[1][1][4] + x[i+m-1+k]*b[1][1][5] + x[i+m+k]*b[1][1][6];
//            double gamma = sum;
//            double x_new = -(gamma) / b[1][1][0];
//            double newdx = abs(x_new - x[i+k]);
//            if (newdx > maxdx)
//                maxdx = newdx;
//            x[i+k] = x_new;
//        }
    }
    return maxdx;
}

void GaussSeidelSolver::showGraph()
{
    QObject *par;
    par = new QObject();
    QProcess proc(par);

    proc.setWorkingDirectory(qApp->applicationDirPath() + QDir::separator());
    proc.start("python build-graph.py");
    proc.waitForStarted();
    proc.write(QString("%1\n").arg(1).toStdString().c_str());
    proc.write(QString("%1\n").arg(n).toStdString().c_str());
    for (int i = 0; i < 2*k; i++)
    {
        proc.waitForBytesWritten();
        proc.write(QString("%1\n").arg(x[i]).toStdString().c_str());
    }
    proc.waitForFinished(-1);

}

double GaussSeidelSolver::updateLagrange(double ro)
{
    return 0;
}

void GaussSeidelSolver::calculateCoefficients()
{
//    if (!Py_IsInitialized())
//        Py_Initialize();
//    FILE * file = fopen("function.py", "r");
//    if (PyRun_SimpleFile(file, "function.py") != 0)
//        cerr << "Error executing Python code" << endl;
//    Py_Finalize();

}
